//////////////////////////////////////////////////////////////////
// Shell
//
// C program shell for rapid application development
// The Flying Pig!
// Started 17/7/2003
//////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////
// Includes

#include "oslib/osmodule.h"
#include "oslib/messagetrans.h"
#include "oslib/wimp.h"
#include "oslib/dragasprite.h"
#include "oslib/macros.h"
#include "oslib/osfile.h"
#include "oslib/resolver.h"
#include "oslib/os.h"
#include "oslib/osbyte.h"
#include "sys/byteorder.h"
#include "sys/select.h"

#include "flexlib/flex.h"

#include "Comp1.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <stddef.h>

#if defined _DEBUG
#include <kernel.h>
#endif

//////////////////////////////////////////////////////////////////
// Defines

#define WORDALIGN(b) (((b) + 3) & ~3)
#define RANGE(var,min,max)   if ((var) < (min)) var = min; \
                             if ((var) > (max)) var = max;

#define MENU_XPOS_OFFSET     (-64)
/* (96 + 44 * <menu items> + 24 * <menu separators>) */
#define ICONBAR_MENU_YPOS    (96 + 44 * 2 + 24 * 0)
#define RAMTRANSMIT_SIZE     (10 * 1024)
#define FILE_LOAD_MAX        (64)
#define POLL_DELAY           (10)
#define SEND_SMALL           (LINK_SEND_MAX)
#define SEND_LARGE           (1024*20)
#define LINKFILENAME_MAX     (RAMTRANSMIT_SIZE)
#define DATAIN_CHUNK_SIZE    (1024*5)

#define COMMAND_LEN          (1024)
#define BUFFERSIZE           (1024*5)
#define COMMAND_LEN_QUEUE    (1032*10)
#define LISTEN_BACKLOG       (2)
#define DEFAULT_PORT         (6435)

//////////////////////////////////////////////////////////////////
// Structures

typedef enum
{
  STATE_INVALID = -1,

  STATE_DISCONNECTED,

  STATE_CREATESOCKET,
  STATE_RESOLVING,
  STATE_CONNECT,
  STATE_LISTEN,
  STATE_CONNECTING,
  STATE_ACCEPT,
  STATE_CONNECTED,

  STATE_CANCEL,
  STATE_FINISHED,
  STATE_FINISHPAUSE,

  STATE_NUM
} STATE;

typedef enum
{
  SOCK_INVALID = -1,

  SOCK_OK,
  SOCK_ERR,
  SOCK_FAIL,
  SOCK_WAIT,
  SOCK_RECEIVE,
  SOCK_MULTIDONE,
  SOCK_CLOSED,

  SOCK_NUM
} SOCK;

//////////////////////////////////////////////////////////////////
// Global variables

static char                   gszProgTitle[128];
static char                   gpcTemp[256];
static char                   *gpcMessages;
//static wimp_w                 gwhMain;
//static wimp_w                 gwhSave;
static wimp_w                 gwhConf;
static wimp_w                 gwhWarn;
static wimp_w                 gwhStat;
//static wimp_i                 gihIconBarIcon;
static int                    gnMenuWins = 0;
static wimp_w                 gawhMenuWinHandle[10];
static int                    gnMenuXpos = 0;
static int                    gnMenuYpos = 0;
static wimp_menu              *gpcMenuCurrent = NULL;
static wimp_menu              *gpcIconBarMenu;
static wimp_menu              *gpcWindowMenu;
static wimp_t                 gnTaskHandle;
static bool                   gboDrag = FALSE;
static char                   gacFontRef[255];
static int                    gnMessageMyRef = 0;
//static SAVETYPE               geSaveType = SAVETYPE_INVALID;
//static LOADTYPE               geLoadType = LOADTYPE_INVALID;
static int                    gnSaveFileType;
//static char                   gszSaveString[255];
static char                   *gpcExample;
static int                    gnExampleSize;

//static wimp_message_data_xfer gsMessageStore;
//static wimp_t                 gthSender;
//static char                   gpcRamTransBuffer[RAMTRANSMIT_SIZE];
//static int                    gnMyRef;

//static int                    gnRamTransRef;
//static char *                 gpcRamTransPos;
//static int                    gnRamTransLeft;

static int                    gnLinkHandle = -1;
static wimp_t                 ghLinkTask = NULL;
static char                   gpcLinkBuffer[LINK_RAM_BUFFER];
static int                    gnLinkTransRef = -1;
static char                   *gpcLinkTransPos = NULL;
static int                    gnLinkTransLeft = 0;


static int                    gnSendSize;

//static char                   *gapcLoaded[FILE_LOAD_MAX];
//static int                    ganLoadedSize[FILE_LOAD_MAX];
//static int                    gnLoadedNum = 0;
//static int                    gnDataSend = -1;
//static char                   gszFileName[LINKFILENAME_MAX];
//static int                    gnFileNamePos;
//static bool                   gboFileNameGood;

static char                   *gapcDataOut[FILE_LOAD_MAX];
static int                    ganDataOutSize[FILE_LOAD_MAX];
static int                    ganDataOutPos[FILE_LOAD_MAX];
static int                    gnDataOutNum;

static char                   gszServer[COMMAND_LEN - 5] = "127.0.0.1";
static int                    gnServerLen;
static int                    gnPort = DEFAULT_PORT;
static int                    gnAddress;
static socket_s               gsSocket;
static STATE                  geState = STATE_INVALID;
static STATE                  gePrevState = STATE_INVALID;
static char                   gpcBuffer[BUFFERSIZE];
static int                    gnSendCommandLen;
static int                    gnSendCommandSent;
static socket_s               gsSendSocket;
static char                   gszSendCommand[COMMAND_LEN_QUEUE];
static socket_s               gsSendSocket;
static socket_sockaddr        gsClientAddr;
static bool                   gboLinkOpen = FALSE;
static int                    gnConnectAttempt = 0;
static int                    gnSent;
static int                    gnReceived;
static char                   gszStatus[10];

void ConfigSetUpWindow (void);
void ConfigSetFromWindow (void);

void NetworkLoop (void);
SOCK CreateSocket (socket_s * psSocket);
STATE Connect (int nAddress, socket_s * psSocket, int nPort);
STATE CheckConnected (socket_s sSocket);
void PrintState (STATE eState);
STATE Resolve (char * szHostName, int * pnAddress);
int SendDataStart (char * * ppcData, int nSize, socket_s * psSocket);
SOCK SendDataUpdate (void);
void Cancel (void);
SOCK SocketRead (socket_s * psSocket);
STATE ImmediateUpdate (socket_s * psSocket);
STATE Connected (socket_s * psSocket);
STATE Listen (socket_s * psSocket);
STATE Accept (socket_s * psSocket);
void ResetStatus (void);

//////////////////////////////////////////////////////////////////
// Main application

// Respond to swi errors
inline void err (os_error * sError)
{
  if (sError)
  {
    ShowWarning (sError);
  }
}

// Main program
int main (/*int argc, char * * argv*/)
{
  wimp_block                  cBlock;
  wimp_version_no             nVersion;
  int                         nCount;
  wimp_event_no               nEvent;
  wimp_MESSAGE_LIST(10) sMessages = {{
//                                      message_DATA_SAVE,
//                                      message_DATA_SAVE_ACK,
//                                      message_DATA_LOAD,
//                                      message_DATA_OPEN,
//                                      message_RAM_FETCH,
//                                      message_RAM_TRANSMIT,
                                      message_HELP,
                                      message_URL_LAUNCH,
                                      message_LINK_CONTROL,
                                      message_LINK_OPEN,
                                      message_LINK_CLOSE,
                                      message_LINK_SEND,
                                      message_LINK_DATASAVE,
                                      message_LINK_RAMFETCH,
                                      message_LINK_RAMTRANSMIT,
                                      0u}};
  wimp_message_list *psUserMessages = (wimp_message_list*)&sMessages;
  os_t                        nTime;

  // Load Messages file
  gpcMessages = osmodule_alloc (17 + sizeof(MESSAGES));
  strcpy(gpcMessages + 16, MESSAGES);

  messagetrans_open_file ((messagetrans_control_block*)gpcMessages,
    gpcMessages + 16, 0);

  // Initialise task
  gnTaskHandle = wimp_initialise (wimp_VERSION_RO3, Tag ("Tsk"),
    psUserMessages, & nVersion);

  // Initialise the flex memory
  strncpy (gszProgTitle, Tag("Tsk"), sizeof (gszProgTitle));
  flex_init (gszProgTitle, 0, 0);

  // Load templates
  for (nCount = 0; nCount < 256; nCount++)
  {
    gacFontRef[nCount] = 0;
  }
  wimp_open_template (TEMPLATES);
  gwhWarn = LoadTemplate ("Warning");
  gwhConf = LoadTemplate ("Config");
  gwhStat = LoadTemplate ("Status");

  // Set up general variables
  gnDataOutNum = 0;
  gnSendSize = SEND_SMALL;

  gnLinkTransRef = -1;
  gpcLinkTransPos = NULL;
  gnLinkTransLeft = 0;

  gnSendCommandLen = 0;
  gnSendCommandSent = 0;

  geState = STATE_DISCONNECTED;
  gePrevState = STATE_INVALID;
  gboLinkOpen = FALSE;
  gnPort = DEFAULT_PORT;
  gnConnectAttempt = 0;
  gnSent = 0;
  gnReceived = 0;
  gnServerLen = 0;

  // Create menus
//  gpcIconBarMenu = CreateMenu (Tag ("Menu1"));
//  gpcWindowMenu = CreateMenu (Tag("Menu2"));

//  SetSubMenu (gpcWindowMenu, 0, (wimp_menu*)gwhSave);

  // Close templates
  wimp_close_template ();

  // Set up the version info
//  SetIconText (VERSION_STRING_LONG, gawhMenuWinHandle[0], 3);

  // Create icon
//  gihIconBarIcon = CreateIconbarIcon (Tag ("Icn"), 76, 76);

  // Main polling loop
  //////////////////////////////////////////////////////////////////
  do
  {
    nTime = os_read_monotonic_time () + POLL_DELAY;
    nEvent = wimp_poll_idle (FLAGS, & cBlock, nTime, NULL);
    switch (nEvent)
    {
      case  0: // Null
        NullPoll (& cBlock);
        break;
      case  1: // Redraw window
        break;
      case  2: // Open window
        OpenWindow (& cBlock);
        break;
      case  3: // Closewindow
        CloseWindow (& cBlock);
        break;
      case  4: // Pointer leaving window
        break;
      case  5: // Pointer entering window
        break;
      case  6:
        MouseClick (& cBlock);
        break;
      case  7: // User drag box
//        UserDragBox (& cBlock);
        break;
      case  8: // Keys
        Keys (& cBlock);
        break;
      case  9:
//        MenuSelect (& cBlock);
        break;
      case 10: // Scroll request
        break;
      case 11: // Lose caret
        break;
      case 12: // Gain caret
        break;
      case 17: // Receive message
      case 18:
        Receive (& cBlock);
        break;
      case 19: // UserMessage Acknowledge
        Acknowledged (& cBlock);
        break;
      default: // Default
        break;
    }
  } while TRUE;

  //////////////////////////////////////////////////////////////////

  return 0;
}

//////////////////////////////////////////////////////////////////
// Poll 16,17: Message received
void Receive (wimp_block *pcBlock)
{
  switch (pcBlock->message.action)
  {
    case message_QUIT:
      Quit ();
      break;
    case message_DATA_SAVE:
//      DataSave (pcBlock);
      break;
    case message_DATA_SAVE_ACK:
//      DataSaveAck (pcBlock);
      break;
    case message_DATA_LOAD:
//      DataLoad (pcBlock);
      break;
    case message_DATA_OPEN:
//      DataOpen (pcBlock);
      break;
    case message_RAM_FETCH:
//      RamFetch (pcBlock);
      break;
    case message_RAM_TRANSMIT:
//      RamTransmit (pcBlock);
      break;
    case message_HELP:
      Help (pcBlock);
      break;

    case message_LINK_CONTROL:
      ReceiveLinkControl (pcBlock);
      break;
    case message_LINK_OPEN:
      ReceiveLinkOpen (pcBlock);
      break;
    case message_LINK_CLOSE:
      ReceiveLinkClose (pcBlock);
      break;
    case message_LINK_SEND:
      ReceiveLinkSend (pcBlock);
      break;
    case message_LINK_DATASAVE:
      ReceiveLinkDataSave (pcBlock);
      break;
    case message_LINK_RAMFETCH:
      ReceiveLinkRamFetch (pcBlock);
      break;
    case message_LINK_RAMTRANSMIT:
      ReceiveLinkRamTransmit (pcBlock);
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Poll 19: Message acknowledge received
void Acknowledged (wimp_block *pcBlock)
{
  switch (pcBlock->message.action)
  {
    case message_RAM_FETCH:
//      RamFetchReturned (pcBlock);
      break;
    case message_URL_LAUNCH:
      OpenURLReturned (pcBlock);
      break;
  }
  gnMessageMyRef = 0;
}

//////////////////////////////////////////////////////////////////
// Quit task
void Quit (void)
{
  int                         nCount;
  int                         nRemove;

  // Free the fonts
  for (nCount = 0; nCount < 256; nCount++)
  {
    if (gacFontRef[nCount])
    {
      for (nRemove = 0; nRemove < gacFontRef[nCount]; nRemove++)
      {
        xfont_lose_font ((font_f)nCount);
      }
    }
  }

  // Close any open sockets
  if (geState != STATE_DISCONNECTED)
  {
    geState = STATE_FINISHED;
    NetworkLoop ();
  }

  // Close the program
  wimp_close_down (gnTaskHandle);
}

//////////////////////////////////////////////////////////////////
// Poll 6: Process mouse clicks
void MouseClick (wimp_block *pcBlock)
{
//  int                         nXpos       = pcBlock->pointer.pos.x;
//  int                         nYpos       = pcBlock->pointer.pos.y;
  wimp_mouse_state            nButton     = pcBlock->pointer.buttons;
  wimp_w                      whWindow    = pcBlock->pointer.w;
  wimp_i                      ihIcon      = pcBlock->pointer.i;
//  char                        *szFileIcon;

//  if (whWindow == wimp_ICON_BAR)
//  {
//    switch (nButton)
//    {
//      case wimp_CLICK_SELECT:
//        OpenWindowInit(gwhMain);
//        break;
//      case wimp_CLICK_MENU:
//        OpenMenu (gpcIconBarMenu, nXpos + MENU_XPOS_OFFSET,
//          ICONBAR_MENU_YPOS);
//        break;
//    }
//  }
//
//  if (whWindow == gwhMain)
//  {
//    if (nButton == wimp_CLICK_MENU)
//    {
//      OpenMenu (gpcWindowMenu, nXpos - 64, nYpos);
//    }
//    else
//    {
//      switch (ihIcon)
//      {
//        default: // Save drag
//          if (nButton & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST))
//          {
//            strncpy (gszSaveString, GetIconText (whWindow, ihIcon),
//              sizeof(gszSaveString));
//            DragBox (whWindow, ihIcon);
//            geSaveType = SAVETYPE_ICONTEXT;
//          }
//          break;
//      }
//    }
//  }
//
//  if (whWindow == gwhSave)
//  {
//    switch (ihIcon)
//    {
//      case (wimp_i)3: // Save drag
//        if (nButton & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST))
//        {
//          szFileIcon = GetIconText (gwhSave, 3);
//          gnSaveFileType = 0xfff;
//          DragSprite (whWindow, ihIcon, "file_fff");
//          geSaveType = SAVETYPE_FILE;
//        }
//        break;
//      case (wimp_i)1: // Cancel
//        CloseMenu ();
//        break;
//      case (wimp_i)0: // Save
//        gnSaveFileType = 0xfff;
//        SaveSave ();
//        if (nButton != wimp_CLICK_ADJUST)
//        {
//          CloseMenu ();
//        }
//        break;
//    }
//  }

  if (whWindow == gwhWarn)
  {
    switch (ihIcon)
    {
      case (wimp_i)0: // OK
        CloseWindowHandle (gwhWarn);
        break;
    }
  }

  if (whWindow == gwhConf)
  {
    switch (ihIcon)
    {
      case (wimp_i)0: // Cancel
        ConfigSetUpWindow ();
        if (nButton & wimp_CLICK_SELECT)
        {
          CloseWindowHandle (gwhConf);
        }
        break;
      case (wimp_i)1: // OK
        ConfigSetFromWindow ();
        if (nButton & wimp_CLICK_SELECT)
        {
          CloseWindowHandle (gwhConf);
        }
        break;
    }
  }

//  if (whWindow == gawhMenuWinHandle[0])
//  {
//    switch (ihIcon)
//    {
//      case (wimp_i)8: // Email
//        OpenURL (Tag("Email"));
//        if (nButton != wimp_CLICK_ADJUST)
//        {
//          CloseMenu ();
//        }
//        break;
//      case (wimp_i)9: // Website
//        OpenURL (Tag("Website"));
//        if (nButton != wimp_CLICK_ADJUST)
//        {
//          CloseMenu ();
//        }
//        break;
//    }
//  }
}

//////////////////////////////////////////////////////////////////
// Poll 8: Act on user key presses
void Keys (wimp_block *pcBlock)
{
  wimp_w                      whWindow;
  wimp_i                      ihIcon;
  wimp_key_no                 nKey;

  whWindow = pcBlock->key.w;
  ihIcon = pcBlock->key.i;
  nKey = pcBlock->key.c;

  switch (nKey)
  {
    case wimp_KEY_RETURN:
//      if (whWindow == gwhSave)
//      {
//        gnSaveFileType = 0xfff;
//        SaveSave ();
//        CloseMenu ();
//      }
      if (whWindow == gwhWarn)
      {
        CloseWarning ();
      }
      if (whWindow == gwhConf)
      {
        ConfigSetFromWindow ();
        CloseWindowHandle (gwhConf);
      }
      break;
    case wimp_KEY_ESCAPE:
      if (whWindow == gwhWarn)
      {
        CloseWarning ();
      }
      if (whWindow == gwhConf)
      {
        ConfigSetUpWindow ();
        CloseWindowHandle (gwhConf);
      }
      break;
    default:
      xwimp_process_key (nKey);
      break;
  }
}

////////////////////////////////////////////////////////////////////
//// Poll 9: Act on menu selections
//void MenuSelect (wimp_block *pcBlock)
//{
//  wimp_pointer                sPointer;
//
//  xwimp_get_pointer_info (& sPointer);
//
//  if (gpcMenuCurrent == gpcIconBarMenu)
//  {
//    switch (pcBlock->selection.items[0])
//    {
//      case 1:
//        Quit ();
//        break;
//      default:
//        break;
//    }
//  }
//
//  if (gpcMenuCurrent == gpcWindowMenu)
//  {
//    switch (pcBlock->selection.items[0])
//    {
//      case 1:
//        Quit ();
//        break;
//      default:
//        break;
//    }
//  }
//
//  if (sPointer.buttons & wimp_CLICK_ADJUST)
//  {
//    OpenMenu (gpcMenuCurrent, gnMenuXpos, gnMenuYpos);
//  }
//}

////////////////////////////////////////////////////////////////////
//// Poll 7: User has finished dragging an object
//void UserDragBox (wimp_block *pcBlock)
//{
//  char                        *szFilename;
//  wimp_w                      whWindow;
//  wimp_pointer                sPointer;
//  wimp_message                sMessage;
//
//  pcBlock = pcBlock;
//
//  xdragasprite_stop ();
//  gboDrag = FALSE;
//
//  xwimp_get_pointer_info (& sPointer);
//
//  whWindow = sPointer.w;
//
//  if ((whWindow != gwhMain)
//     && (whWindow != gwhSave)
//     && (whWindow != gwhWarn))
//  {
//    switch (geSaveType)
//    {
//      case SAVETYPE_FILE:
//        sMessage.data.data_xfer.w = whWindow;
//        sMessage.data.data_xfer.i = sPointer.i;
//        sMessage.data.data_xfer.pos = sPointer.pos;
//        sMessage.data.data_xfer.est_size = gnExampleSize;
//        sMessage.data.data_xfer.file_type = gnSaveFileType;
//
//        szFilename = GetIconText (gwhSave, 2);
//        while (strchr (szFilename, '.'))
//        {
//          szFilename = strchr(szFilename, '.') + 1;
//        }
//
//        strncpy (sMessage.data.data_xfer.file_name, szFilename, 211);
//        sMessage.data.data_xfer.file_name[211] = 0;
//
//        sMessage.size = WORDALIGN((44 + 1 + strlen (szFilename)));
//        sMessage.your_ref = 0;
//        sMessage.action = 1;
//
//        xwimp_send_message_to_window (wimp_USER_MESSAGE, & sMessage,
//                   whWindow, sPointer.i, NULL);
//        break;
//      case SAVETYPE_ICONTEXT:
//        sMessage.data.data_xfer.w = whWindow;
//        sMessage.data.data_xfer.i = sPointer.i;
//        sMessage.data.data_xfer.pos = sPointer.pos;
//        sMessage.data.data_xfer.est_size = strlen(gszSaveString) + 1;
//        sMessage.data.data_xfer.file_type = 0xfff;
//
//        strncpy (sMessage.data.data_xfer.file_name, "IconText", 12);
//        sMessage.data.data_xfer.file_name[11] = 0;
//
//        sMessage.size = WORDALIGN((44 + 12));
//        sMessage.your_ref = 0;
//        sMessage.action = 1;
//
//        xwimp_send_message_to_window (wimp_USER_MESSAGE, & sMessage,
//          whWindow, sPointer.i, NULL);
//        break;
//      default:
//        break;
//    }
//  }
//}

//////////////////////////////////////////////////////////////////
// Poll 2: Open window given window block
void OpenWindow (wimp_block *pcBlock)
{
  xwimp_open_window (& pcBlock->open);
}

//////////////////////////////////////////////////////////////////
// Open a window on screen that is currently closed
void OpenWindowInit(wimp_w whWindow)
{
  wimp_window_state           sState;

  sState.w = whWindow;
  xwimp_get_window_state (& sState);
  sState.next = wimp_TOP;
  xwimp_open_window ((wimp_open *)& sState);
}

//////////////////////////////////////////////////////////////////
// Open a window in the centre of the screen
void OpenWindowInitCentre(wimp_w whWindow)
{
  int                         nHeight;
  int                         nWidth;
  int                         nEigFactor;
  int                         nSize;
  int                         nPosition;
  wimp_window_state           sState;

  sState.w = whWindow;
  xwimp_get_window_state (& sState);
  sState.next = wimp_TOP;

  nWidth = sState.visible.x1 - sState.visible.x0;
  nHeight = sState.visible.y1 - sState.visible.y0;

  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_XEIG_FACTOR,
    & nEigFactor, NULL);
  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_XWIND_LIMIT,
    & nSize, NULL);
  nPosition = ((nSize << nEigFactor) - nWidth) / 2;
  sState.visible.x0 = nPosition;
  sState.visible.x1 = nPosition + nWidth;

  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_YEIG_FACTOR,
    & nEigFactor, NULL);
  xos_read_mode_variable (os_CURRENT_MODE, os_MODEVAR_YWIND_LIMIT,
    & nSize, NULL);
  nPosition = ((nSize << nEigFactor) - nHeight) / 2;
  sState.visible.y0 = nPosition;
  sState.visible.y1 = nPosition + nHeight;

  xwimp_open_window ((wimp_open *)& sState);
}

//////////////////////////////////////////////////////////////////
// Open a given menu at the given position on screen
void OpenMenu (wimp_menu * pcMenu, int nXpos, int nYpos)
{
  xwimp_create_menu (pcMenu, nXpos, nYpos);

  gnMenuXpos = nXpos;
  gnMenuYpos = nYpos;
  gpcMenuCurrent = pcMenu;
}

//////////////////////////////////////////////////////////////////
// Poll 3: Close a window
void CloseWindow (wimp_block *pcBlock)
{
  wimp_close_window (pcBlock->close.w);
}

//////////////////////////////////////////////////////////////////
// Close window given handle
void CloseWindowHandle (wimp_w whWindow)
{
  wimp_close_window (whWindow);
}

//////////////////////////////////////////////////////////////////
// Close the currently open menu
void CloseMenu (void)
{
  xwimp_create_menu ((wimp_menu*)-1, 0, 0);

  gnMenuXpos = 0;
  gnMenuYpos = 0;
  gpcMenuCurrent = NULL;
}

//////////////////////////////////////////////////////////////////
// Create an iconbar icon
wimp_i CreateIconbarIcon (char * szSprite, int nWidth, int nHeight)
{
  wimp_i                      ihIcon;
  wimp_icon_create            sIconBlock;

  sIconBlock.w = wimp_ICON_BAR_RIGHT;
  sIconBlock.icon.extent.x0 = 0;
  sIconBlock.icon.extent.y0 = 0;
  sIconBlock.icon.extent.x1 = nWidth;
  sIconBlock.icon.extent.y1 = nHeight;
  sIconBlock.icon.flags = wimp_ICON_SPRITE |
                   (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT);
  strncpy(sIconBlock.icon.data.sprite, szSprite, 12);

  ihIcon = wimp_create_icon (& sIconBlock);

  return ihIcon;
}

//////////////////////////////////////////////////////////////////
// Create window from Templates file
wimp_w LoadTemplate (char * szWindowTitle)
{
  wimp_window                 *pcWindow;
  char *                      pcIndirected;
  int                         nWindowSize;
  int                         nIndirectedSize;
  char                        szTitle[12];
  wimp_w                      whWindow;

  wimp_load_template (0, NULL, NULL, NULL, szWindowTitle, 0,
    & nWindowSize, & nIndirectedSize);

  pcWindow = (wimp_window*)malloc (nWindowSize);
  pcIndirected = malloc (nIndirectedSize);
  strncpy (szTitle, szWindowTitle, 12);

  wimp_load_template (pcWindow, pcIndirected, pcIndirected + nIndirectedSize,
    gacFontRef, szWindowTitle, 0, NULL, NULL);

  whWindow = wimp_create_window (pcWindow);

  free((void *)pcWindow);

  return whWindow;
}

//////////////////////////////////////////////////////////////////
// Create window from Templates file with sprites
wimp_w LoadTemplateSprites (char * szWindowTitle, osspriteop_area * pcSpriteArea)
{
  wimp_window                 *pcWindow;
  char *                      pcIndirected;
  int                         nWindowSize;
  int                         nIndirectedSize;
  char                        szTitle[12];
  wimp_w                      whWindow;

  wimp_load_template (0, NULL, NULL, NULL, szWindowTitle, 0,
    & nWindowSize, & nIndirectedSize);

  pcWindow = (wimp_window*)malloc (nWindowSize);
  pcIndirected = malloc (nIndirectedSize);
  strncpy (szTitle, szWindowTitle, 12);

  wimp_load_template (pcWindow, pcIndirected, pcIndirected + nIndirectedSize,
    gacFontRef, szWindowTitle, 0, NULL, NULL);

  if (pcSpriteArea)
  {
    pcWindow->sprite_area = pcSpriteArea;
  }

  whWindow = wimp_create_window (pcWindow);

  free((void *)pcWindow);

  return whWindow;
}

//////////////////////////////////////////////////////////////////
// Create a menu from a text string - usually kept in the messages file
wimp_menu * CreateMenu (char * szMenu)
{
  wimp_menu                   *pcMenu;
  char *                      pcBuffer;
  char *                      nAt = 0;
  char *                      nAt2 = 0;
  int                         nWidth = 0;
  wimp_menu_flags             uFlags;
  char                        szMenuString[512];
  char *                      pcMenuString;
  int                         nMenuItems;
  char                        *szItem;
  int                         nMenuEntry;
  int                         nStringLen;

  nMenuItems = -1;
  szItem = szMenu;
  while (szItem)
  {
    nMenuItems++;
    szItem = strchr(szItem + 1, ',');
  }

  pcMenu = (wimp_menu*)malloc (wimp_SIZEOF_MENU(nMenuItems));
  pcBuffer = (char *)malloc (strlen(szMenu));

  nAt = strchr (szMenu, ',');
  if (!nAt)
  {
    nAt = (char *)strlen (szMenu);
  }
  else
  {
    nAt -= (int)szMenu;
  }
  strncpy (szMenuString, szMenu, (int)nAt);
  szMenuString[(int)nAt] = 0;

  if (strlen (szMenuString) >= 12)
  {
    strcpy (pcBuffer, szMenuString);
    pcMenu->title_data.indirected_text.text = pcBuffer;
    pcBuffer += strlen (szMenuString) + 1;
    uFlags = wimp_MENU_TITLE_INDIRECTED;
  }
  else
  {
    strncpy (pcMenu->title_data.text, szMenuString, 12);
    uFlags = 0u;
  }
  pcMenu->title_fg = wimp_COLOUR_BLACK;
  pcMenu->title_bg = wimp_COLOUR_LIGHT_GREY;
  pcMenu->work_fg = wimp_COLOUR_BLACK;
  pcMenu->work_bg = wimp_COLOUR_WHITE;

  pcMenu->height = 44;
  pcMenu->gap = 0;

  nMenuEntry = 0;
  do
  {
    pcMenu->entries[nMenuEntry].sub_menu = (wimp_menu*)-1;
    pcMenu->entries[nMenuEntry].icon_flags = wimp_ICON_TEXT
                   | wimp_ICON_FILLED
                   | (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT);
    nAt2 = nAt + 1;

    nAt = strchr (szMenu + (int)nAt2, ',');
    if (!nAt)
    {
      nAt = (char *)strlen (szMenu) + 1;
    }
    else
    {
      nAt -= (int)szMenu;
    }
    strncpy (szMenuString, szMenu + (int)nAt2, (int)nAt - (int)nAt2);
    szMenuString[(int)nAt - (int)nAt2] = 0;

    pcMenuString = szMenuString;

    if (pcMenuString[0] == '+')
    {
      uFlags = wimp_MENU_TICKED;
      pcMenuString++;
    }
    if (pcMenuString[0] == '-')
    {
      uFlags |= wimp_MENU_SEPARATE;
      pcMenuString++;
    }
    if (pcMenuString[0] == '|')
    {
      uFlags |= wimp_MENU_WRITABLE;
      pcMenuString++;
    }
    if (pcMenuString[0] == '>')
    {
      pcMenuString++;
      if (gnMenuWins < (int)sizeof(gawhMenuWinHandle))
      {
        gawhMenuWinHandle[gnMenuWins] = LoadTemplate (pcMenuString);
        pcMenu->entries[nMenuEntry].sub_menu
                   = (wimp_menu*)gawhMenuWinHandle[gnMenuWins];

        gnMenuWins++;
      }
    }

    nStringLen = (int)strlen (pcMenuString);
    if (strlen (pcMenuString) >= 12)
    {
      pcMenu->entries[nMenuEntry].icon_flags |= wimp_ICON_INDIRECTED;
      pcMenu->entries[nMenuEntry].data.indirected_text.text = pcBuffer;
      pcMenu->entries[nMenuEntry].data.indirected_text.validation = NULL;
      pcMenu->entries[nMenuEntry].data.indirected_text.size = nStringLen + 1;
      strcpy (pcBuffer, pcMenuString);
      pcBuffer += nStringLen + 1;
    }
    else
    {
      strncpy (pcMenu->entries[nMenuEntry].data.text, pcMenuString, 12);
    }

    pcMenu->entries[nMenuEntry].menu_flags = uFlags;
    if (nStringLen > nWidth)
    {
      nWidth = nStringLen;
    }
    nMenuEntry++;
    uFlags = 0u;
  } while ((int)nAt < (int)strlen (szMenu));

  pcMenu->width = (nWidth + 1) * 16;
  pcMenu->entries[nMenuEntry - 1].menu_flags |= wimp_MENU_LAST;

  return pcMenu;
}

//////////////////////////////////////////////////////////////////
// Attach a submenu to a menu
void SetSubMenu (wimp_menu * pcMain, int nItem, wimp_menu * pcSub)
{
  pcMain->entries[nItem].sub_menu = pcSub;
}

//////////////////////////////////////////////////////////////////
// Set the greyed out status of a menu item
void SetMenuItemGreyness (bool boState, wimp_menu * pcMenu, int nItem)
{
  if (boState)
  {
    pcMenu->entries[nItem].icon_flags |= wimp_ICON_SHADED;
  }
  else
  {
    pcMenu->entries[nItem].icon_flags &= ~wimp_ICON_SHADED;
  }
}

//////////////////////////////////////////////////////////////////
// Set the ticked status of a menu item
void SetMenuItemTicked (bool boTicked, wimp_menu * pcMenu, int nItem)
{
  if (boTicked)
  {
    pcMenu->entries[nItem].menu_flags |= wimp_MENU_TICKED;
  }
  else
  {
    pcMenu->entries[nItem].menu_flags &= ~wimp_MENU_TICKED;
  }
}

//////////////////////////////////////////////////////////////////
// Get the text string from an icon
char * GetIconText (wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_state             sIconState;
  char                        *szString;
  int                         nTerminate = 0;

  sIconState.w = whWindow;
  sIconState.i = ihIcon;
  xwimp_get_icon_state (& sIconState);

  if (sIconState.icon.flags & wimp_ICON_INDIRECTED)
  {
    szString = sIconState.icon.data.indirected_text.text;
    while (szString[nTerminate] >= 32)
    {
      nTerminate++;
    }
    szString[nTerminate] = 0;
  }
  else
  {
    szString = sIconState.icon.data.text;
    while ((szString[nTerminate] >= 32) && (nTerminate < 12))
    {
      nTerminate++;
    }
    szString[nTerminate] = 0;
  }
  return szString;
}

//////////////////////////////////////////////////////////////////
// Set the text string for an icon
void SetIconText (char * szText, wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_state             sIconState;

  sIconState.w = whWindow;
  sIconState.i = ihIcon;
  xwimp_get_icon_state (& sIconState);

  if (sIconState.icon.flags & wimp_ICON_INDIRECTED)
  {
    strcpy (sIconState.icon.data.indirected_text.text, szText);
  }
  else
  {
    strncpy (sIconState.icon.data.text, szText, 12);
    sIconState.icon.data.text[11] = 0;
  }

  xwimp_set_icon_state (whWindow, ihIcon, 0, 0);
}

//////////////////////////////////////////////////////////////////
// Get the selection state from an icon
bool GetIconSelectionState (wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_state             sIconState;
  bool                        boState;

  sIconState.w = whWindow;
  sIconState.i = ihIcon;
  xwimp_get_icon_state (& sIconState);

  if (sIconState.icon.flags & wimp_ICON_SELECTED)
  {
    boState = TRUE;
  }
  else
  {
    boState = FALSE;
  }

  return boState;
}

//////////////////////////////////////////////////////////////////
// Set the selection state from an icon
void SetIconSelectionState (bool boState, wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_flags             uFlagsEOR;

  if (boState)
  {
    uFlagsEOR = wimp_ICON_SELECTED;
  }
  else
  {
    uFlagsEOR = 0;
  }

  xwimp_set_icon_state (whWindow, ihIcon, uFlagsEOR, wimp_ICON_SELECTED);
}

//////////////////////////////////////////////////////////////////
// Grey out or ungrey an icon
void SetIconGreyness (bool boState, wimp_w whWindow, wimp_i ihIcon)
{
  wimp_icon_flags             uFlagsEOR;

  if (boState)
  {
    uFlagsEOR = wimp_ICON_SHADED;
  }
  else
  {
    uFlagsEOR = 0;
  }

  xwimp_set_icon_state (whWindow, ihIcon, uFlagsEOR, wimp_ICON_SHADED);
}

////////////////////////////////////////////////////////////////////
//// Sender wants to send data to the receiver
//// Normal use: user has terminated a drag, so this message is sent
//// Response:   DataSaveAck
////             RamFetch
//void DataSave (wimp_block *pcBlock)
//{
//  // Check filetype
//  if (pcBlock->message.data.data_xfer.file_type == 0xfff)
//  {
//    // Make a copy in case the RamFetch returns;
//    gsMessageStore = pcBlock->message.data.data_xfer;
//    gthSender = pcBlock->message.sender;
//
//    pcBlock->message.size = 28;
//    pcBlock->message.your_ref = pcBlock->message.my_ref;
//    pcBlock->message.action = message_RAM_FETCH;
//    pcBlock->message.data.ram_xfer.addr = gpcRamTransBuffer;
//    pcBlock->message.data.ram_xfer.size = RAMTRANSMIT_SIZE;
//
//    xwimp_send_message (wimp_USER_MESSAGE_RECORDED, & pcBlock->message,
//      pcBlock->message.sender);
//
//    gnMyRef = pcBlock->message.my_ref;
//    geLoadType = LOADTYPE_FILE;
//
//    // We're going to send this data on, so we need to open a link
//    LinkOpen (0);
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// RamFetch Acknowledge received - try DataSaveAck instead
//// Usually followed by a DataLoad message
//void RamFetchReturned (wimp_block *pcBlock)
//{
//  if (gnMyRef == pcBlock->message.my_ref)
//  {
//    // Use the copy made earlier during the DataSave
//    pcBlock->message.data.data_xfer = gsMessageStore;
//
//    pcBlock->message.size = 60;
//    pcBlock->message.your_ref = pcBlock->message.my_ref;
//    pcBlock->message.action = message_DATA_SAVE_ACK;
//    strcpy (pcBlock->message.data.data_xfer.file_name, "<Wimp$Scrap>");
//
//    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//      gthSender);
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// Acknowledge that you received a DataSave message
//// Usually followed by a DataLoad message
//void DataSaveAck (wimp_block *pcBlock)
//{
//  char                        *szFilename;
//  int                         nStringLen;
//
//  CloseMenu ();
//
//  switch (geSaveType)
//  {
//      case SAVETYPE_FILE:
//        szFilename = pcBlock->message.data.data_xfer.file_name;
//        // Save the file to the given filename
//        SetIconText (szFilename, gwhSave, 2);
//
//        // Save the file
//        SaveFile (szFilename);
//
//        pcBlock->message.your_ref = pcBlock->message.my_ref;
//        pcBlock->message.action = message_DATA_LOAD;
//
//        xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//          pcBlock->message.sender);
//        break;
//      case SAVETYPE_ICONTEXT:
//        szFilename = pcBlock->message.data.data_xfer.file_name;
//        // Save the file to the given filename
//        nStringLen = strlen (gszSaveString);
//
//        err (xosfile_save_stamped (szFilename, 0xfff, gszSaveString,
//          (gszSaveString + nStringLen)));
//
//        pcBlock->message.your_ref = pcBlock->message.my_ref;
//        pcBlock->message.action = message_DATA_LOAD;
//
//        xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//          pcBlock->message.sender);
//        break;
//      default:
//        break;
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// Receiver of this message should load the file
//// and answer with DataSaveAck if successful
//void DataLoad (wimp_block *pcBlock)
//{
//  char                        *szFilename;
////  char                        *pcLoadedFile;
////  int                         nSize;
//
//  // Check filetype
//  if (pcBlock->message.data.data_xfer.file_type == 0xfff)
//  {
//    szFilename = pcBlock->message.data.data_xfer.file_name;
//
//    // Load the file from the given filename
//    LoadFileFlex (szFilename, & pcLoadedFile, & nSize);
//
//    // Delete the file
//    if (strcmp (szFilename, "<Wimp$Scrap>") == 0)
//    {
//      err (xosfile_delete (szFilename, NULL, NULL, NULL, NULL, NULL));
//    }
//
//    if (pcLoadedFile)
//    {
//      pcBlock->message.your_ref = pcBlock->message.my_ref;
//      pcBlock->message.action = message_DATA_LOAD_ACK;
//
//      xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//        pcBlock->message.sender);
//
//      FileLoadedFlex (& pcLoadedFile, nSize);
//
//      // Now we're done with the file
//      if (pcLoadedFile)
//      {
//        flex_free ((flex_ptr)(& pcLoadedFile));
//        pcLoadedFile = NULL;
//      }
//    }
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// User has double clicked on a file
//void DataOpen (wimp_block *pcBlock)
//{
//  char                        *szFilename;
//  char                        *pcLoadedFile;
//  int                         nSize;
//
//  // Check filetype
//  if (pcBlock->message.data.data_xfer.file_type == 0xffd)
//  {
//    szFilename = pcBlock->message.data.data_xfer.file_name;
//
//    // Load the file from the given filename
//    LoadFileFlex (szFilename, & pcLoadedFile, & nSize);
//
//    if (pcLoadedFile)
//    {
//      pcBlock->message.your_ref = pcBlock->message.my_ref;
//      pcBlock->message.action = message_DATA_LOAD_ACK;
//
//      xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//        pcBlock->message.sender);
//
//      FileLoadedFlex (& pcLoadedFile, nSize);
//
//      // Now we're done with the file
//      if (pcLoadedFile)
//      {
//        flex_free ((flex_ptr)(& pcLoadedFile));
//        pcLoadedFile = NULL;
//      }
//    }
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// Copy the data from here to the external program
//// Reply with RamTransmit
//void RamFetch (wimp_block *pcBlock)
//{
//  wimp_t                      nDestHandle;
//  int                         nSize;
//  int                         nSendLen;
//
//  nSize = pcBlock->message.data.ram_xfer.size;
//  nDestHandle = pcBlock->message.sender;
//
//  if (gnRamTransRef != pcBlock->message.your_ref)
//  {
//    // First in a series of RAM transfers
//    CloseMenu ();
//
//    switch (geSaveType)
//    {
//      case SAVETYPE_FILE:
//        // Save out the example file
//        gpcRamTransPos = gpcExample;
//        gnRamTransLeft = gnExampleSize;
//        gnRamTransRef = pcBlock->message.your_ref;
//        break;
//      case SAVETYPE_ICONTEXT:
//        gpcRamTransPos = gszSaveString;
//        gnRamTransLeft = (int)strlen (gszSaveString);
//        gnRamTransRef = pcBlock->message.your_ref;
//        break;
//      default:
//        gnRamTransRef = -1;
//        break;
//    }
//  }
//
//  if ((gnRamTransRef == pcBlock->message.your_ref) && (gpcRamTransPos))
//  {
//    // Continue with a previously started RAM transfer
//    // (it might have been started just a few lines of code before this!)
//    if (gnRamTransLeft < nSize)
//    {
//      // This will be the last transfer
//      nSendLen = gnRamTransLeft;
//    }
//    else
//    {
//      // There will be more to transfer after this
//      nSendLen = nSize;
//    }
//
//    if (nSendLen > 0)
//    {
//      xwimp_transfer_block (gnTaskHandle, gpcRamTransPos, nDestHandle,
//        pcBlock->message.data.ram_xfer.addr, nSendLen);
//    }
//
//    pcBlock->message.size = 28;
//    pcBlock->message.your_ref = pcBlock->message.my_ref;
//    pcBlock->message.action = message_RAM_TRANSMIT;
//    pcBlock->message.data.ram_xfer.size = nSendLen;
//
//    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//      pcBlock->message.sender);
//
//    gpcRamTransPos += nSendLen;
//    gnRamTransLeft -= nSendLen;
//
//    if (nSendLen < nSize)
//    {
//      gnRamTransRef = -1;
//    }
//    else
//    {
//      gnRamTransRef = pcBlock->message.my_ref;
//    }
//  }
//}
//
////////////////////////////////////////////////////////////////////
//// Response to a RamFetch
//void RamTransmit (wimp_block *pcBlock)
//{
//  char *                      pcLoadedFile;
//  int                         nSize;
//  int                         nSuccess = 1;
//
//  if (gnMyRef == pcBlock->message.your_ref)
//  {
//    pcLoadedFile = pcBlock->message.data.ram_xfer.addr;
//    nSize = pcBlock->message.data.ram_xfer.size;
//
//    if (nSize != 0)
//    {
//      switch (geLoadType)
//      {
//        case LOADTYPE_FILE:
//          if (gpcLoadedFile == NULL)
//          {
//            // The first transfer
//            // Create some flex memory
//            nSuccess = flex_alloc ((flex_ptr)(& gpcLoadedFile), nSize);
//            if (nSuccess == 1)
//            {
//              // Copy the data
//              memcpy (gpcLoadedFile, pcLoadedFile, nSize);
//              gnLoadedFileSize = nSize;
//            }
//          }
//          else
//          {
//            // A continuation of the transfer
//            // Extend the flex memory
//            nSuccess = flex_extend ((flex_ptr)(& gpcLoadedFile),
//              gnLoadedFileSize + nSize);
//            if (nSuccess == 1)
//            {
//              // Copy the data
//              memcpy (gpcLoadedFile + gnLoadedFileSize, pcLoadedFile, nSize);
//              gnLoadedFileSize += nSize;
//            }
//            else
//            {
//              // Something went wrong, so we'd better just free up the
//              // memory
//              flex_free ((flex_ptr)(& gpcLoadedFile));
//              gpcLoadedFile = NULL;
//              gnLoadedFileSize = 0;
//            }
//          }
//
//          if ((nSize < RAMTRANSMIT_SIZE) && (nSuccess == 1))
//          {
//            // The last transfer
//            // Save the file out, for the sake of the example
//            FileLoadedFlex (& gpcLoadedFile, gnLoadedFileSize);
//
//            // Now we're done with the file
//            if (gpcLoadedFile)
//            {
//              flex_free ((flex_ptr)(& gpcLoadedFile));
//              gpcLoadedFile = NULL;
//            }
//            gnLoadedFileSize = 0;
//          }
//        default:
//          break;
//      }
//    }
//
//    if (nSuccess == 1)
//    {
//      if (nSize == RAMTRANSMIT_SIZE)
//      {
//        // The buffer was full, so we must ask for some more
//        pcBlock->message.size = 28;
//        pcBlock->message.your_ref = pcBlock->message.my_ref;
//        pcBlock->message.action = message_RAM_FETCH;
//        pcBlock->message.data.ram_xfer.addr = gpcRamTransBuffer;
//        pcBlock->message.data.ram_xfer.size = RAMTRANSMIT_SIZE;
//
//        xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
//          pcBlock->message.sender);
//
//        gnMyRef = pcBlock->message.my_ref;
//      }
//      else
//      {
//        // This was the final transfer
//        geLoadType = LOADTYPE_INVALID;
//      }
//    }
//    else
//    {
//      // There was a problem allocating memory
//      ShowWarningTag ("Er2");
//      geLoadType = LOADTYPE_INVALID;
//    }
//  }
//}

//////////////////////////////////////////////////////////////////
// Response to a Help message
void Help (wimp_block *pcBlock)
{
  wimp_w                      whWindow;
  wimp_i                      ihIcon;
  char                        szHelpText[236];
  int                         nStrLen;
  char                        szTag[256];
  int                         nWinNum = 0;
  wimp_selection              sSelection;
  bool                        boGeneral;

  whWindow = *(wimp_w*)(pcBlock->message.data.reserved + 12);
  ihIcon = *(wimp_i*)(pcBlock->message.data.reserved + 16);

  // Warning window
  if (whWindow == gwhWarn)
  {
    nWinNum = 3;
  }

  if (nWinNum)
  {
    // Window
    szHelpText[0] = 0;
    sprintf (szTag, "H%dI%d", nWinNum, ihIcon);
    strncpy (szHelpText, TagCheck (szTag), sizeof(szHelpText));
    nStrLen = strlen(szHelpText);
    if (nStrLen == 0)
    {
      sprintf (szTag, "H%d", nWinNum);
      strncpy (szHelpText, TagCheck (szTag), sizeof(szHelpText));
    }
    szHelpText[sizeof(szHelpText) - 1] = 0;
    nWinNum = -1;
  }
  else
  {
    // Menu
    wimp_get_menu_state (wimp_GIVEN_WINDOW_AND_ICON, & sSelection,
      whWindow, ihIcon);

    boGeneral = FALSE;
    if (sSelection.items[0] != -1)
    {
      // Iconbar menu
      if (gpcMenuCurrent == gpcIconBarMenu)
      {
        nWinNum = 6;
      }

      // Main window menu
      if (gpcMenuCurrent == gpcWindowMenu)
      {
        nWinNum = 7;
      }
    }
    if (nWinNum)
    {
      if (boGeneral)
      {
        sprintf (szTag, "H%d", nWinNum);
      }
      else
      {
        if (sSelection.items[1] != -1)
        {
          sprintf (szTag, "H%dI%dI%d", nWinNum, sSelection.items[0],
            sSelection.items[1]);
        }
        else
        {
          sprintf (szTag, "H%dI%d", nWinNum, sSelection.items[0]);
        }
      }

      strncpy (szHelpText, TagCheck (szTag), sizeof(szHelpText));
      nWinNum = -1;
    }
  }

  if (nWinNum == -1)
  {
    pcBlock->message.size = WORDALIGN((24 + strlen(szHelpText)));
    pcBlock->message.your_ref = pcBlock->message.my_ref;
    pcBlock->message.action = 0x503;
    strncpy (pcBlock->message.data.reserved, szHelpText, sizeof(szHelpText));

    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
      pcBlock->message.sender);
  }
}

//////////////////////////////////////////////////////////////////
// Obtain string from Messages file
char * Tag (char * szTag)
{
  messagetrans_lookup ((messagetrans_control_block*)gpcMessages,
    szTag, gpcTemp, 256, 0, 0, 0, 0, NULL);

  return gpcTemp;
}

//////////////////////////////////////////////////////////////////
// Obtain string from Messages file if it exists
char * TagCheck (char * szTag)
{
  osbool                      boMore = FALSE;
  int                         nUsed = 0;
  char                        *szResult;
  os_error                    *psError;

  xmessagetrans_enumerate_tokens ((messagetrans_control_block*)gpcMessages,
    szTag, gpcTemp, 256, 0, & boMore, & nUsed, NULL);

  if (!boMore)
  {
    strcpy (gpcTemp, "");
    szResult = gpcTemp;
  }
  else
  {
    psError = xmessagetrans_lookup ((messagetrans_control_block*)gpcMessages,
      szTag, gpcTemp, 256, NULL, NULL, NULL, NULL, & szResult, & nUsed);

    if (psError)
    {
      strcpy (gpcTemp, "");
      szResult = gpcTemp;
    }
  }

  return szResult;
}

//////////////////////////////////////////////////////////////////
// Load a file into a block of memory
char * LoadFile (char * szFilename, int * pnSize)
{
  char                        *pcMemory;
  int                         nSize;
  os_error                    *psError;
  fileswitch_object_type      eObjectType;

  xosfile_read_stamped_no_path (szFilename, & eObjectType, NULL, NULL,
    & nSize, NULL, NULL);

  if ((eObjectType != fileswitch_NOT_FOUND) && (nSize > 0))
  {
    if (pnSize)
    {
      *pnSize = nSize;
    }
    pcMemory = malloc (nSize);

    if (pcMemory)
    {
      psError = xosfile_load_stamped_no_path (szFilename, pcMemory, NULL,
        NULL, NULL, NULL, NULL);

      if (psError)
      {
        free (pcMemory);
        pcMemory = NULL;
        err (psError);
      }
    }
    else
    {
      ShowWarningTag ("Er3");
    }
  }
  else
  {
    pcMemory = NULL;
  }

  return pcMemory;
}

//////////////////////////////////////////////////////////////////
// Load a file into a block of flex memory
int LoadFileFlex (char * szFilename, char * * ppcMemory, int * pnSize)
{
  int                         nSize;
  int                         nSuccess = 0;
  fileswitch_object_type      eObjectType;
  os_error                    *psError;

  xosfile_read_stamped_no_path (szFilename, & eObjectType, NULL, NULL,
    & nSize, NULL, NULL);

  if ((eObjectType != fileswitch_NOT_FOUND) && (nSize > 0))
  {
    if (pnSize)
    {
      *pnSize = nSize;
    }
    nSuccess = flex_alloc ((flex_ptr) ppcMemory, nSize);

    if ((nSuccess == 1) && (*ppcMemory))
    {
      psError = xosfile_load_stamped_no_path (szFilename, * ppcMemory, NULL,
        NULL, NULL, NULL, NULL);

      if (psError)
      {
        flex_free ((flex_ptr) ppcMemory);
        *ppcMemory = NULL;
        nSuccess = -1;
        err (psError);
      }
    }
    else
    {
      ShowWarningTag ("Er3");
      nSuccess = 0;
      *ppcMemory = NULL;
    }
  }
  else
  {
    nSuccess = -1;
    *ppcMemory = NULL;
  }

  return nSuccess;
}

//////////////////////////////////////////////////////////////////
// Load a sprite file into a block of memory
osspriteop_area * LoadSprites (char * szFilename)
{
  osspriteop_area             *pcMemory;
  int                         nSize;

  xosfile_read_stamped_no_path (szFilename, NULL, NULL, NULL, & nSize,
    NULL, NULL);

  if (nSize > 0)
  {
    pcMemory = (osspriteop_area*)malloc (nSize);

    if (pcMemory)
    {
      pcMemory->size = nSize;
      pcMemory->sprite_count = 0;
      pcMemory->first = 16;
      pcMemory->used = 16;
      xosspriteop_clear_sprites (osspriteop_USER_AREA, pcMemory);
      err (xosspriteop_load_sprite_file (osspriteop_USER_AREA, pcMemory,
        szFilename));
    }
  }
  else
  {
    pcMemory = NULL;
  }

  return pcMemory;
}

//////////////////////////////////////////////////////////////////
// Display an error from the Messages file in a warning box
void ShowWarningTag (char * szTag)
{
    xos_bell  ();
    SetIconText (Tag(szTag), gwhWarn, 1);
    OpenWindowInitCentre (gwhWarn);
    CloseMenu ();

    xwimp_set_caret_position (gwhWarn, wimp_ICON_WINDOW, 0, 0, -1, -1);
}

//////////////////////////////////////////////////////////////////
// Display an error in a warning box
void ShowWarning (os_error * sError)
{
    xos_bell  ();
    SetIconText (sError->errmess, gwhWarn, 1);
    OpenWindowInitCentre (gwhWarn);
    CloseMenu ();
    REPORT (sError->errmess);

    xwimp_set_caret_position (gwhWarn, wimp_ICON_WINDOW, 0, 0, -1, -1);
}

//////////////////////////////////////////////////////////////////
// Close the warning message box
void CloseWarning (void)
{
  CloseWindowHandle (gwhWarn);
}

//////////////////////////////////////////////////////////////////
// Initiate a sprite drag from the contents of the given icon
void DragSprite (wimp_w whWindow, wimp_i ihIcon, char * szIconName)
{
  osspriteop_area             *pcSpriteArea;
  int                         nXinc;
  int                         nYinc;
  wimp_icon_state             sIconState;
  wimp_window_info            sWindowInfo;

  if (!gboDrag)
  {
    sIconState.w = whWindow;
    sIconState.i = ihIcon;
    xwimp_get_icon_state (& sIconState);

    if (((sIconState.icon.flags & (wimp_ICON_TEXT
      | wimp_ICON_SPRITE | wimp_ICON_INDIRECTED))
      == (wimp_ICON_SPRITE | wimp_ICON_INDIRECTED)))
    {
      pcSpriteArea = sIconState.icon.data.indirected_sprite.area;
    }
    else
    {
      pcSpriteArea = NULL;
    }

    sWindowInfo.w = whWindow;
    xwimp_get_window_info_header_only (& sWindowInfo);

    if (!pcSpriteArea)
    {
      pcSpriteArea = sWindowInfo.sprite_area;
    }

    nXinc = sWindowInfo.visible.x0 - sWindowInfo.xscroll;
    nYinc = sWindowInfo.visible.y1 - sWindowInfo.yscroll;

    sIconState.icon.extent.x0 += nXinc;
    sIconState.icon.extent.y0 += nYinc;
    sIconState.icon.extent.x1 += nXinc;
    sIconState.icon.extent.y1 += nYinc;

    xdragasprite_start ((dragasprite_HPOS_CENTRE | dragasprite_VPOS_CENTRE
      | dragasprite_BOUND_POINTER | dragasprite_DROP_SHADOW),
      pcSpriteArea, szIconName, & sIconState.icon.extent, NULL);

    gboDrag = TRUE;
  }
}

//////////////////////////////////////////////////////////////////
// Initiate a outline drag from the dimensions of the given icon
void DragBox (wimp_w whWindow, wimp_i ihIcon)
{
  int                         nXinc;
  int                         nYinc;
  wimp_icon_state             sIconState;
  wimp_window_state           sWindowState;
  wimp_drag                   sDragInfo;

  if (!gboDrag)
  {
    sIconState.w = whWindow;
    sIconState.i = ihIcon;
    xwimp_get_icon_state (& sIconState);

    sWindowState.w = whWindow;
    xwimp_get_window_state (& sWindowState);

    nXinc = sWindowState.visible.x0 - sWindowState.xscroll;
    nYinc = sWindowState.visible.y1 - sWindowState.yscroll;

    sDragInfo.w = 0;
    sDragInfo.type = wimp_DRAG_USER_FIXED;
    sDragInfo.initial.x0 = sIconState.icon.extent.x0 + nXinc;
    sDragInfo.initial.y0 = sIconState.icon.extent.y0 + nYinc;
    sDragInfo.initial.x1 = sIconState.icon.extent.x1 + nXinc;
    sDragInfo.initial.y1 = sIconState.icon.extent.y1 + nYinc;
    sDragInfo.bbox.x0 = 0;
    sDragInfo.bbox.y0 = 0;
    sDragInfo.bbox.x1 = 0;
    sDragInfo.bbox.y1 = 0;
    sDragInfo.handle = NULL;
    sDragInfo.draw = NULL;
    sDragInfo.undraw = NULL;
    sDragInfo.redraw = NULL;

    xwimp_drag_box (& sDragInfo);

    gboDrag = TRUE;
  }
}

//////////////////////////////////////////////////////////////////
// Initiate a URL link
void OpenURL (char * szURL)
{
  wimp_message                sMessage;

  sMessage.your_ref = 0;
  sMessage.action = 0x4af80;

  strcpy ((char *)sMessage.data.reserved, szURL);

  sMessage.size = WORDALIGN(strlen(szURL) + 21);

  xwimp_send_message (wimp_USER_MESSAGE_RECORDED, & sMessage,
    wimp_BROADCAST);

  gnMessageMyRef = sMessage.my_ref;
}

//////////////////////////////////////////////////////////////////
// No URL lancher responded, so we have to run one ourselves
void OpenURLReturned (wimp_block *pcBlock)
{
  char                        szAlias[255];
  char                        szVar[255];
  int                         nUsed;

  if (gnMessageMyRef == pcBlock->message.my_ref)
  {
    if (strncmp (pcBlock->message.data.reserved, "mailto:", 7) == 0)
    {
      strcpy (szAlias, "URLOpen_MailTo");
    }
    else
    {
      strcpy (szAlias, "URLOpen_HTTP");
    }

    sprintf (szVar, "Alias$%s", szAlias);
    xos_read_var_val (szVar, 0, -1, 0, os_VARTYPE_STRING, & nUsed,
      NULL, NULL);

    if (nUsed)
    {
      sprintf (szVar, "%s %s", szAlias, pcBlock->message.data.reserved);
      err (xwimp_start_task (szVar, NULL));
    }
  }
}

//// Save box save button clicked
//void SaveSave (void)
//{
//  char                        *szFilename;
//
//  szFilename = GetIconText (gwhSave, 2);
//
//  if (strchr(szFilename, '.'))
//  {
//    // Save the file
//    SaveFile (szFilename);
//  }
//  else
//  {
//    ShowWarningTag ("Er1");
//  }
//}

#if defined _DEBUG
//////////////////////////////////////////////////////////////////
// Report a debug message
void Report (char * szMessage)
{
  _kernel_swi_regs            sRegs;

  sRegs.r[0] = (int)szMessage;
  _kernel_swi (Report_Text0, & sRegs, & sRegs);
}

//////////////////////////////////////////////////////////////////
// Report a debug message with variable
void ReportVar (char * szFormat, int nVariable)
{
  _kernel_swi_regs            sRegs;
  char                        szReport[255];

  sprintf (szReport, szFormat, nVariable);
  sRegs.r[0] = (int)szReport;
  _kernel_swi (Report_Text0, & sRegs, & sRegs);
}
#endif // _DEBUG

//////////////////////////////////////////////////////////////////
// Save file routine - saves the example file out
void SaveFile (char * szFilename)
{
  err (xosfile_save_stamped (szFilename, gnSaveFileType,  gpcExample,
    (gpcExample + gnExampleSize)));
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_CONTROL
void ReceiveLinkControl (wimp_block *pcBlock)
{
  int                         nAction;

  nAction = ((int *)(pcBlock->message.data.reserved))[1];

  switch (nAction)
  {
    case 0: // Init
      gnLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
      ghLinkTask = pcBlock->message.sender;
      ActOnInit ();
      break;
    case 1: // Config
      ActOnConfig ();
      break;
  }

  REPORTVAR ("RECV- LinkControl handle = %d", gnLinkHandle);
  REPORTVAR ("      LinkControl action = %d", nAction);
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_OPEN
void ReceiveLinkOpen (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];

    ActOnLinkOpen (nLink);

    REPORTVAR ("RECV: LinkOpen handle = %d", nLinkHandle);
    REPORTVAR ("      LinkOpen link = %d", nLink);
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_SEND
void ReceiveLinkSend (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;
  int                         nSize;
  char                        *pcData;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];
    nSize = ((int *)(pcBlock->message.data.reserved))[2];
    pcData =  pcBlock->message.data.reserved + 12;

    ActOnLinkSend (nLink, pcData, nSize);

    REPORTVAR ("RECV: LinkSend handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkSend link = %d", nLink);
    REPORTVAR ("      LinkSend size = %d", nSize);
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_CLOSE
void ReceiveLinkClose (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];

    ActOnLinkClose (nLink);

    REPORTVAR ("RECV: LinkClose handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkClose link = %d", nLink);
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_DATASAVE
void ReceiveLinkDataSave (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];

    REPORTVAR ("RECV: LinkDataSave handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkDataSave link = %d", nLink);

    // Reply with a RamFetch message
    ((int *)(pcBlock->message.data.reserved))[2] = (int)gpcLinkBuffer;
    ((int *)(pcBlock->message.data.reserved))[3] = LINK_RAM_BUFFER;

    pcBlock->message.size = 36;
    pcBlock->message.your_ref = pcBlock->message.my_ref;
    pcBlock->message.action = message_LINK_RAMFETCH;

    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
      pcBlock->message.sender);

    REPORTVAR ("SEND: LinkRamFetch handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkRamFetch link = %d", nLink);
    REPORTVAR ("      LinkRamFetch size = %d", LINK_RAM_BUFFER);
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_RAMFETCH
void ReceiveLinkRamFetch (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;
  int                         nSize;
  char                        *pcBuffer;
  int                         nSendLen;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];
    pcBuffer = (char *)(((int *)(pcBlock->message.data.reserved))[2]);
    nSize = ((int *)(pcBlock->message.data.reserved))[3];

    REPORTVAR ("RECV: LinkRamFetch handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkRamFetch link = %d", nLink);
    REPORTVAR ("      LinkRamFetch size = %d", nSize);

    // Transfer data
    if (gnLinkTransLeft < nSize)
    {
      nSendLen = gnLinkTransLeft;
    }
    else
    {
      nSendLen = nSize;
    }

    if (nSendLen > 0)
    {
      xwimp_transfer_block (gnTaskHandle, gpcLinkTransPos,
        pcBlock->message.sender, pcBuffer, nSendLen);
    }

    // Reply with a RamTransmit message
    ((int *)(pcBlock->message.data.reserved))[2] = (int)pcBuffer;
    ((int *)(pcBlock->message.data.reserved))[3] = nSendLen;

    pcBlock->message.size = 36;
    pcBlock->message.your_ref = pcBlock->message.my_ref;
    pcBlock->message.action = message_LINK_RAMTRANSMIT;

    xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
      pcBlock->message.sender);

    REPORTVAR ("SEND: LinkRamTransmit handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkRamTransmit link = %d", nLink);
    REPORTVAR ("      LinkRamTransmit size = %d", nSendLen);

    gpcLinkTransPos += nSendLen;
    gnLinkTransLeft -= nSendLen;

    if (nSendLen < nSize)
    {
      // Everything has been transmitted
      gnLinkTransRef = -1;

      ActOnLinkSent (nLink);
    }
    else
    {
      gnLinkTransRef = pcBlock->message.my_ref;
    }
  }
}

//////////////////////////////////////////////////////////////////
// Receive message LINK_RAMTRANSMIT
void ReceiveLinkRamTransmit (wimp_block *pcBlock)
{
  int                         nLinkHandle;
  int                         nLink;
  int                         nSize;
  char                        *pcBuffer;

  nLinkHandle = ((int *)(pcBlock->message.data.reserved))[0];
  if (nLinkHandle == gnLinkHandle)
  {
    nLink = ((int *)(pcBlock->message.data.reserved))[1];
    nSize = ((int *)(pcBlock->message.data.reserved))[3];
    pcBuffer = (char *)(((int *)(pcBlock->message.data.reserved))[2]);

    REPORTVAR ("RECV: LinkRamTransmit handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkRamTransmit link = %d", nLink);
    REPORTVAR ("      LinkRamTransmit size = %d", nSize);

    if (nSize > 0)
    {
      ActOnLinkSend (nLink, pcBuffer, nSize);
    }

    if (nSize == LINK_RAM_BUFFER)
    {
      // The buffer was full, so we should ask for some more

      // Reply with a RamFetch message
      ((int *)(pcBlock->message.data.reserved))[2] = (int)gpcLinkBuffer;
      ((int *)(pcBlock->message.data.reserved))[3] = LINK_RAM_BUFFER;
      ((int *)(pcBlock->message.data.reserved))[4] = 0;

      pcBlock->message.size = 40;
      pcBlock->message.your_ref = pcBlock->message.my_ref;
      pcBlock->message.action = message_LINK_RAMFETCH;

      xwimp_send_message (wimp_USER_MESSAGE, & pcBlock->message,
        pcBlock->message.sender);

      REPORTVAR ("SEND: LinkRamFetch handle = %d", gnLinkHandle);
      REPORTVAR ("      LinkRamFetch link = %d", nLink);
      REPORTVAR ("      LinkRamFetch size = %d", LINK_RAM_BUFFER);
    }
  }
}

//////////////////////////////////////////////////////////////////
// Send message LINK_OPEN
void LinkOpen (int nLink)
{
  wimp_message                sMessage;

  if (ghLinkTask != NULL)
  {
    ((int *)(sMessage.data.reserved))[0] = gnLinkHandle;
    ((int *)(sMessage.data.reserved))[1] = nLink;

    sMessage.size = 28;
    sMessage.your_ref = 0;
    sMessage.action = message_LINK_OPEN;

    xwimp_send_message (wimp_USER_MESSAGE, & sMessage, ghLinkTask);

    REPORTVAR ("SEND: LinkOpen handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkOpen link = %d", nLink);
  }
}

//////////////////////////////////////////////////////////////////
// Send message LINK_SEND
void LinkSend (int nLink, char * pcData, int nSize)
{
  wimp_message                sMessage;

  if (ghLinkTask != NULL)
  {
    if (nSize <= LINK_SEND_MAX)
    {
      // Can be sent in a single message
      ((int *)(sMessage.data.reserved))[0] = gnLinkHandle;
      ((int *)(sMessage.data.reserved))[1] = nLink;
      ((int *)(sMessage.data.reserved))[2] = nSize;
      memcpy (sMessage.data.reserved + 12, pcData, nSize);

      sMessage.size = WORDALIGN ((32 + nSize));
      sMessage.your_ref = 0;
      sMessage.action = message_LINK_SEND;

      xwimp_send_message (wimp_USER_MESSAGE, & sMessage, ghLinkTask);

      ActOnLinkSent (nLink);

      REPORTVAR ("SEND: LinkSend handle = %d", gnLinkHandle);
      REPORTVAR ("      LinkSend link = %d", nLink);
      REPORTVAR ("      LinkSend size = %d", nSize);
    }
    else
    {
      // Needs to be sent using ram transfers
      ((int *)(sMessage.data.reserved))[0] = gnLinkHandle;
      ((int *)(sMessage.data.reserved))[1] = nLink;
      ((int *)(sMessage.data.reserved))[2] = nSize;

      sMessage.size = 32;
      sMessage.your_ref = 0;
      sMessage.action = message_LINK_DATASAVE;

      xwimp_send_message (wimp_USER_MESSAGE, & sMessage, ghLinkTask);

      REPORTVAR ("SEND: LinkDataSave handle = %d", gnLinkHandle);
      REPORTVAR ("      LinkDataSave link = %d", nLink);
      REPORTVAR ("      LinkDataSave size = %d", nSize);

      gpcLinkTransPos = pcData;
      gnLinkTransLeft = nSize;
    }
  }
}

//////////////////////////////////////////////////////////////////
// Send message LINK_CLOSE
void LinkClose (int nLink)
{
  wimp_message                sMessage;

  if (ghLinkTask != NULL)
  {
    ((int *)(sMessage.data.reserved))[0] = gnLinkHandle;
    ((int *)(sMessage.data.reserved))[1] = nLink;

    sMessage.size = 28;
    sMessage.your_ref = 0;
    sMessage.action = message_LINK_CLOSE;

    xwimp_send_message (wimp_USER_MESSAGE, & sMessage, ghLinkTask);

    REPORTVAR ("SEND: LinkClose handle = %d", gnLinkHandle);
    REPORTVAR ("      LinkClose link = %d", nLink);
  }
}

//////////////////////////////////////////////////////////////////
// Set up the configuration window
void ConfigSetUpWindow (void)
{
  char                        szText[1024];

  SetIconText (gszServer, gwhConf, 3);

  sprintf (szText, "%d", gnPort);
  SetIconText (szText, gwhConf, 5);
}

//////////////////////////////////////////////////////////////////
// Set configuration from the configuration window
void ConfigSetFromWindow (void)
{
  strncpy (gszServer, GetIconText (gwhConf, 3), sizeof (gszServer));
  gszServer[sizeof (gszServer) - 1] = 0;
  sscanf (GetIconText (gwhConf, 5), "%d", & gnPort);
}

//////////////////////////////////////////////////////////////////
// Act on initialisation
void ActOnInit (void)
{
  gboLinkOpen = FALSE;
  gnConnectAttempt = 0;
  gnServerLen = 0;

  OpenWindowInitCentre (gwhStat);
  ResetStatus ();
}

//////////////////////////////////////////////////////////////////
// Act on config request
void ActOnConfig (void)
{
  ConfigSetUpWindow ();
  OpenWindowInitCentre (gwhConf);
  xwimp_set_caret_position (gwhConf, (wimp_i)3, 0, 0, -1, -1);
}

//////////////////////////////////////////////////////////////////
// Act on the opening of a link
void ActOnLinkOpen (int nLink)
{
  int                         nSuccess;

  switch (nLink)
  {
    case 0: // Data to send
      ResetStatus ();
      nSuccess = flex_alloc ((flex_ptr)(& gapcDataOut[gnDataOutNum]),
        DATAIN_CHUNK_SIZE);
      if (nSuccess == 1)
      {
        ganDataOutPos[gnDataOutNum] = 0;
        ganDataOutSize[gnDataOutNum] = DATAIN_CHUNK_SIZE;
      }
      else
      {
        ganDataOutPos[gnDataOutNum] = -1;
        ShowWarningTag ("Er5");
      }
      break;
    case 1: // Address
      gnServerLen = 0;
      gszServer[0] = 0;
      break;
    default:
      ganDataOutPos[gnDataOutNum] = -1;
      ShowWarningTag ("Er6");
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Act on the closing of a link
void ActOnLinkClose (int nLink)
{
  switch (nLink)
  {
    case 0: // Data to send
      if (gnDataOutNum < FILE_LOAD_MAX)
      {
        gnDataOutNum++;
      }
      else
      {
        ShowWarningTag ("Er8");
      }
      break;
    case 1: // Address
      // Do nothing
      break;
    default:
      // Do nothing
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Act on the receiving of data on a link
void ActOnLinkSend (int nLink, char * pcData, int nSize)
{
  int                         nSuccess;
  int                         nNewSize;
  int                         nTotalLen;

  switch (nLink)
  {
    case 0: // Data to send
      if (ganDataOutPos[gnDataOutNum] >= 0)
      {
        // Extend the memory slot if necessary
        if ((ganDataOutPos[gnDataOutNum] + nSize)
          >= ganDataOutSize[gnDataOutNum])
        {
          // Allocate some more memory
          nNewSize = (((ganDataOutPos[gnDataOutNum] + nSize)
            / DATAIN_CHUNK_SIZE) + 1) * DATAIN_CHUNK_SIZE;

          nSuccess = flex_extend ((flex_ptr)(& gapcDataOut[gnDataOutNum]),
            nNewSize);
          if (nSuccess == 1)
          {
            ganDataOutSize[gnDataOutNum] = nNewSize;
          }
          else
          {
            ganDataOutPos[gnDataOutNum] = -1;
            ShowWarningTag ("Er7");
          }
        }

        // Transfer the data into a memory slot
        if ((ganDataOutPos[gnDataOutNum] + nSize)
          < ganDataOutSize[gnDataOutNum])
        {
          memcpy ((gapcDataOut[gnDataOutNum] + ganDataOutPos[gnDataOutNum]),
            pcData, nSize);
          ganDataOutPos[gnDataOutNum] += nSize;
        }
      }
      break;
    case 1: // Address
      // Append the new data onto the end of the current string
      nTotalLen = nSize;
      if ((gnServerLen + nTotalLen) > ((int)sizeof (gszServer) - 1))
      {
        nTotalLen = sizeof (gszServer) - gnServerLen - 1;
      }
      memcpy (gszServer + gnServerLen, pcData, nTotalLen);
      gnServerLen += nTotalLen;
      gszServer[nTotalLen] = 0;
      break;
    default:
      // Do nothing
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Act on the data having been sent
void ActOnLinkSent (int nLink)
{
  nLink = nLink;
}

//////////////////////////////////////////////////////////////////
// Poll 0: Null Poll
void NullPoll (wimp_block *pcBlock)
{
  int                         nCount;
  int                         nSent;
  int                         nPos;
  int                         nNewSize;
  int                         nSuccess;

  NetworkLoop ();

  pcBlock = pcBlock;

  // If there are any files in our queue, we need to send them
  if (gnDataOutNum >= 0)
  {
    if (ganDataOutPos[0] > 0)
    {
      if (geState == STATE_CONNECTED)
      {
        // Send some more data
        nSent = SendDataStart (& gapcDataOut[0], ganDataOutPos[0],
          & gsSocket);

        if (nSent > 0)
        {
          // Remove the data from the memory
          for (nPos = nSent; nPos < ganDataOutPos[0]; nPos++)
          {
            (gapcDataOut[0])[nPos - nSent] = (gapcDataOut[0])[nPos];
          }
          ganDataOutPos[0] -= nSent;

          nNewSize = (((ganDataOutPos[0]) / DATAIN_CHUNK_SIZE) + 1)
            * DATAIN_CHUNK_SIZE;

          if (nNewSize != ganDataOutSize[0])
          {
            nSuccess = flex_extend ((flex_ptr)(& gapcDataOut[0]), nNewSize);
            if (nSuccess == 1)
            {
              ganDataOutSize[0] = nNewSize;
            }
          }
        }
      }
      else
      {
        // Start listening
        if (geState == STATE_DISCONNECTED)
        {
          geState = STATE_CREATESOCKET;
        }
      }
    }

    if ((ganDataOutPos[0] <= 0) && (gnDataOutNum > 0))
    {
      // We've sent everything, so we're done with this file
      flex_free ((flex_ptr)(& gapcDataOut[0]));
      gapcDataOut[0] = NULL;
      ganDataOutSize[0] = 0;
      ganDataOutPos[0] = -1;
      gnDataOutNum--;
      geState = STATE_FINISHED;

      for (nCount = 0; nCount < gnDataOutNum; nCount++)
      {
        flex_reanchor ((flex_ptr)(& gapcDataOut[nCount]),
          (flex_ptr)(& gapcDataOut[nCount + 1]));
        ganDataOutSize[nCount] = ganDataOutSize[nCount + 1];
        ganDataOutPos[nCount] = ganDataOutPos[nCount + 1];
      }
    }
  }
}

//////////////////////////////////////////////////////////////////
// Main network program
void NetworkLoop (void)
{
  SOCK                        eSockError;

  switch (geState)
  {
    case STATE_DISCONNECTED:
      break;
    case STATE_CREATESOCKET:
      SetIconText (gszServer, gwhStat, 5);
      eSockError = CreateSocket (& gsSocket);
      switch (eSockError)
      {
        case SOCK_FAIL:
          Cancel ();
          geState = STATE_CANCEL;
          break;
        case SOCK_ERR:
          Cancel ();
          geState = STATE_CANCEL;
          break;
        default:
          geState = STATE_RESOLVING;
          break;
      }
      break;
    case STATE_RESOLVING:
      geState = Resolve (gszServer, & gnAddress);
      break;
    case STATE_CONNECT:
      geState = Connect (gnAddress, & gsSocket, gnPort);
//      geState = STATE_LISTEN;
      break;
    case STATE_LISTEN:
      geState = Listen (& gsSocket);
      break;
    case STATE_CONNECTING:
      geState = CheckConnected (gsSocket);
      break;
    case STATE_ACCEPT:
      geState = Accept (& gsSocket);
      break;
    case STATE_CONNECTED:
      geState = Connected (& gsSocket);
      SendDataUpdate ();
      break;
    case STATE_CANCEL:
      geState = ImmediateUpdate (& gsSocket);
      break;
    case STATE_FINISHED:
      // Close socket
      err (xsocket_close (gsSocket));
      // Close any open links
      if (gboLinkOpen)
      {
        LinkClose (0);
      }
      SetIconText (gszServer, gwhStat, 5);
      geState = STATE_FINISHPAUSE;
      break;
    case STATE_FINISHPAUSE:
      geState = STATE_DISCONNECTED;
      break;
    default:
      break;
  }

  if (geState != gePrevState)
  {
    PrintState (geState);
    gePrevState = geState;
  }
}

//////////////////////////////////////////////////////////////////
// Create Socket
SOCK CreateSocket (socket_s * psSocket)
{
  socket_sockaddr            sAddress;
  char                       cArg;
  os_error                   *psError;
  SOCK                       eReturn;

  eReturn = SOCK_OK;

  if (psSocket)
  {
    psError = xsocket_creat (socket_AF_INET, socket_SOCK_STREAM,
      socket_IPPROTO_TCP, psSocket);
//      socket_IPPROTO_IP, psSocket);

    if (psError)
    {
      err (psError);
      eReturn = SOCK_FAIL;
    }
    else
    {
      memset (& sAddress, 0, sizeof (sAddress));
      sAddress.sockaddr.af = socket_AF_INET;
      sAddress.sockaddr_in.af = socket_AF_INET;
      sAddress.sockaddr_in.port = htons (0);
      sAddress.sockaddr_in.addr = htonl (0);

      psError = xsocket_bind (*psSocket, & sAddress, 16);

      if (psError)
      {
        err (psError);
        eReturn = SOCK_ERR;
      }
      else
      {
        cArg = 1;
        psError = xsocket_ioctl (*psSocket, socket_FIONBIO, & cArg);

        if (psError)
        {
          err (psError);
          eReturn = SOCK_ERR;
        }
      }
    }
  }
  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Connect to the mail server
STATE Connect (int nAddress, socket_s * psSocket, int nPort)
{
  socket_sockaddr            sAddress;
  os_error                   *psError;
  STATE                      eNextState;

  memset (& sAddress, 0, sizeof (sAddress));
  sAddress.sockaddr.af = socket_AF_INET;
  sAddress.sockaddr_in.af = socket_AF_INET;

  sAddress.sockaddr_in.port = htons (nPort);
//  ((nPort & 0xff) << 8)
//    + ((nPort & 0xff00) >> 8);

  sAddress.sockaddr_in.addr = nAddress;

  psError = xsocket_connect (* psSocket, & sAddress, sizeof (sAddress));

  eNextState = STATE_CONNECTING;
  if (psError)
  {
    if ((psError->errnum == socket_EISCONN)
      || (psError->errnum == (socket_EISCONN + IYONIX_SOCKET_ERR)))
    {
      eNextState = STATE_CONNECTED;
    }
    else
    {
      if ((psError->errnum != socket_EWOULDBLOCK)
        && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
        && (psError->errnum != socket_EALREADY)
        && (psError->errnum != (socket_EALREADY + IYONIX_SOCKET_ERR))
        && (psError->errnum != socket_EINPROGRESS)
        && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
      {
        REPORTVAR ("Error %d", psError->errnum);
        err (psError);
        Cancel ();
//        eNextState = STATE_FINISHED;
        eNextState = STATE_CANCEL;
      }
    }
  }
  else
  {
    eNextState = STATE_CONNECTED;
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Connect to the mail server
STATE CheckConnected (socket_s sSocket)
{
  os_error                   *psError;
  STATE                      eNextState;
  socket_fdset               sRead;
  socket_fdset               sWrite;
  socket_fdset               sExcept;
  int                        nFound;
  socket_timeval             sTimeVal;

  FD_ZERO (& sRead);
  FD_ZERO (& sWrite);
  FD_ZERO (& sExcept);
//  memset (& sRead, 0, sizeof (sRead));
//  memset (& sWrite, 0, sizeof (sWrite));
//  memset (& sExcept, 0, sizeof (sExcept));

  FD_SET ((unsigned int)(sSocket), (fd_set*)(& sRead));
  FD_SET ((unsigned int)(sSocket), (fd_set*)(& sWrite));
  FD_SET ((unsigned int)(sSocket), (fd_set*)(& sExcept));
//  sRead |= (1 << (unsigned int)sSocket);
//  sWrite |= (1 << (unsigned int)sSocket);
//  sExcept |= (1 << (unsigned int)sSocket);

  sTimeVal.sec = 0;
  sTimeVal.usec = 0;

  psError = xsocket_select (((unsigned int)sSocket) + 1, & sRead, & sWrite,
    & sExcept, & sTimeVal, & nFound);

  eNextState = STATE_CONNECTING;
  if (psError)
  {
    REPORTVAR ("Error %d", psError->errnum);
    err (psError);
    Cancel ();
    eNextState = STATE_CANCEL;
  }
  else
  {
#if defined _DEBUG
    if (FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sRead)))
    {
      REPORT ("Found on Read");
    }
    if (FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sWrite)))
    {
      REPORT ("Found on Write");
    }
    if (FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sExcept)))
    {
//      REPORT ("Found on Except");
//      eNextState = STATE_FINISHED;
    }
#endif

    if (nFound > 0)
    {
      if ((FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sRead)))
        || (FD_ISSET ((unsigned int)(sSocket), (fd_set*)(& sWrite))))
      {
        REPORTVAR ("Found %d", nFound);
        eNextState = STATE_CONNECTED;
      }
    }
  }

  return eNextState;
}


//////////////////////////////////////////////////////////////////
// Print the given state
void PrintState (STATE eState)
{
  switch (eState)
  {
    case STATE_DISCONNECTED:
      REPORT ("Disconnected");
      SetIconText (Tag ("St1"), gwhStat, 0);
      break;
    case STATE_CREATESOCKET:
      REPORT ("Creating socket");
      SetIconText (Tag ("St2"), gwhStat, 0);
      break;
    case STATE_RESOLVING:
      REPORT ("Resolving");
      SetIconText (Tag ("St3"), gwhStat, 0);
      break;
    case STATE_CONNECT:
      REPORT ("Connect");
      SetIconText (Tag ("St4"), gwhStat, 0);
      break;
    case STATE_LISTEN:
      REPORT ("Listen");
      SetIconText (Tag ("St5"), gwhStat, 0);
      break;
    case STATE_CONNECTING:
      REPORT ("Connecting");
      SetIconText (Tag ("St6"), gwhStat, 0);
      break;
    case STATE_ACCEPT:
      REPORT ("Accept");
      SetIconText (Tag ("St7"), gwhStat, 0);
      break;
    case STATE_CONNECTED:
      REPORT ("Connected");
      SetIconText (Tag ("St8"), gwhStat, 0);
      break;

    case STATE_CANCEL:
      REPORT ("Cancelled");
      SetIconText (Tag ("St9"), gwhStat, 0);
      break;
    case STATE_FINISHED:
      REPORT ("Finished");
      SetIconText (Tag ("St10"), gwhStat, 0);
      break;
    case STATE_FINISHPAUSE:
      REPORT ("Finish pause");
      SetIconText (Tag ("St11"), gwhStat, 0);
      break;
    default:
      REPORT ("Invalid");
      SetIconText (Tag ("St0"), gwhStat, 0);
      break;
  }
}

//////////////////////////////////////////////////////////////////
// Resolve a host name
STATE Resolve (char * szHostName, int * pnAddress)
{
  _kernel_swi_regs           sRegs;
  STATE                      eNextState;
  os_error                   sError;

  sRegs.r[0] = (int)szHostName;
  _kernel_swi (Resolver_GetHost, & sRegs, & sRegs);

  if (sRegs.r[0] == 0)
  {
    REPORT ("HOST: ");
    REPORT (szHostName);
    eNextState = STATE_CONNECT;
    if (pnAddress)
    {
      *pnAddress = ***(int***)((char*)(sRegs.r[1] + 16));
    }
  }
  else
  {
    if (((int)(sRegs.r[0])) == 36)
    {
      eNextState = STATE_RESOLVING;
    }
    else
    {
      REPORT ("HOST: ");
      REPORT (szHostName);
      sError.errnum = 0;
      strcpy (sError.errmess, "Server lookup failed");
      err (& sError);
      Cancel ();
//      eNextState = STATE_FINISHED;
      eNextState = STATE_CANCEL;
    }
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Read any incoming data from a socket
SOCK SocketRead (socket_s * psSocket)
{
  int                        nRead;
  os_error                   *psError = NULL;
  int                        nBufferSize;
  SOCK                       eReturn = SOCK_WAIT;

  nBufferSize = BUFFERSIZE;
  psError = xsocket_read (*psSocket, gpcBuffer, nBufferSize, & nRead);

  if (!psError)
  {
    // Do stuff with the data
    if (nRead == 0)
    {
      eReturn = SOCK_CLOSED;
    }
    else
    {
      if (!gboLinkOpen)
      {
        LinkOpen (0);
      }
      // Send the data onwards
      LinkSend (0, gpcBuffer, nRead);
      eReturn = SOCK_RECEIVE;

      gnReceived += nRead;
      sprintf (gszStatus, "%d", gnReceived);
      SetIconText (gszStatus, gwhStat, 3);
    }
  }

  if (psError)
  {
    if ((psError->errnum != socket_EWOULDBLOCK)
      && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
      && (psError->errnum != socket_EINPROGRESS)
      && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
    {
      REPORTVAR ("Read Error %d", psError->errnum);
      err (psError);
      eReturn = SOCK_FAIL;
    }
  }

  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Start sending a message
int SendDataStart (char * * ppcData, int nSize, socket_s * psSocket)
{
  os_error                   *psError;
  int                        nReturn;
  int                        nSend;

  nReturn = 0;

  if ((gnSendCommandLen == 0) && (nSize > 0))
  {
    // Set up the command to be sent
    gsSendSocket = * psSocket;

    nSend = nSize;
    if (nSend > COMMAND_LEN_QUEUE)
    {
      nSend = COMMAND_LEN_QUEUE;
    }
    memcpy (gszSendCommand, * ppcData, nSend);

    gnSendCommandLen = nSend;
    gnSendCommandSent = 0;
    psError = xsocket_send (gsSendSocket, gszSendCommand, gnSendCommandLen,
      0, & gnSendCommandSent);

    if (gnSendCommandSent >= gnSendCommandLen)
    {
      gnSendCommandLen = 0;
    }
    nReturn = nSend;

    gnSent += gnSendCommandSent;
    sprintf (gszStatus, "%d", gnSent);
    SetIconText (gszStatus, gwhStat, 1);

    if (psError)
    {
      if ((psError->errnum != socket_EWOULDBLOCK)
        && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
        && (psError->errnum != socket_EINPROGRESS)
        && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
      {
        REPORTVAR ("Send error number %d", psError->errnum);
        err (psError);
        gnSendCommandLen = 0;
        nReturn = 0;
      }
    }
  }

  return nReturn;
}

//////////////////////////////////////////////////////////////////
// Continue sending a message if there is one
SOCK SendDataUpdate (void)
{
  int                        nCc;
  os_error                   *psError;
  SOCK                       eReturn;

  eReturn = SOCK_OK;

  if (gnSendCommandSent < gnSendCommandLen)
  {
    // Continue sending the command
    nCc = 0;
    psError = xsocket_send (gsSendSocket, gszSendCommand + gnSendCommandSent,
      gnSendCommandLen - gnSendCommandSent, 0, & nCc);
    gnSendCommandSent += nCc;

    gnSent += nCc;
    sprintf (gszStatus, "%d", gnSent);
    SetIconText (gszStatus, gwhStat, 1);

    if (gnSendCommandSent < gnSendCommandLen)
    {
      eReturn = SOCK_WAIT;
    }
    else
    {
      gnSendCommandLen = 0;
    }

    if (psError)
    {
      if ((psError->errnum != socket_EWOULDBLOCK)
        && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
        && (psError->errnum != socket_EINPROGRESS)
        && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
      {
        REPORTVAR ("Update error number %d", psError->errnum);
        err (psError);
        gnSendCommandLen = 0;
        eReturn = SOCK_FAIL;
      }
    }
  }

  return eReturn;
}

//////////////////////////////////////////////////////////////////
// Cancels operation
void Cancel (void)
{
  geState = STATE_CANCEL;

  if (gboLinkOpen)
  {
    LinkClose (0);
  }
}

//////////////////////////////////////////////////////////////////
// Handles update state for an error or cancel
STATE ImmediateUpdate (socket_s * psSocket)
{
  STATE                      eNextState = STATE_FINISHED;

  psSocket = psSocket;

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Read data and deal with it
STATE Connected (socket_s * psSocket)
{
  STATE                      eNextState = STATE_CONNECTED;
  SOCK                       eReturn;
  eReturn = SocketRead (psSocket);

  if (eReturn == SOCK_FAIL)
  {
    eNextState = STATE_CANCEL;
  }

  if (eReturn == SOCK_CLOSED)
  {
    eNextState = STATE_FINISHED;
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Set the socket to listen
STATE Listen (socket_s * psSocket)
{
  STATE                      eNextState = STATE_LISTEN;
  os_error                   *psError;

  psError = xsocket_listen (*psSocket, LISTEN_BACKLOG);

  if (psError)
  {
    eNextState = STATE_FINISHED;
  }
  else
  {
    eNextState = STATE_CONNECTING;
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Accept an incoming connection
STATE Accept (socket_s * psSocket)
{
  STATE                      eNextState = STATE_ACCEPT;
  os_error                   *psError;
  socket_s                   sNewSocket;
  int                        nAddrLen;

  memset (& gsClientAddr, 0, sizeof (gsClientAddr));
  nAddrLen = sizeof (gsClientAddr);

  psError = xsocket_accept (*psSocket, & gsClientAddr, & nAddrLen,
    & sNewSocket);

  if (psError)
  {
    if ((psError->errnum != socket_EWOULDBLOCK)
      && (psError->errnum != (socket_EWOULDBLOCK + IYONIX_SOCKET_ERR))
      && (psError->errnum != socket_EINPROGRESS)
      && (psError->errnum != (socket_EINPROGRESS + IYONIX_SOCKET_ERR)))
    {
      REPORTVAR ("Update error number %d", psError->errnum);
      err (psError);
      eNextState = STATE_FINISHED;
    }
  }
  else
  {
    // We're connected!
    LinkOpen (0);
    gboLinkOpen = TRUE;

    eNextState = STATE_CONNECTED;
    // Close the listening connection
    err (xsocket_close (gsSocket));
    gsSocket = sNewSocket;
  }

  return eNextState;
}

//////////////////////////////////////////////////////////////////
// Reset the status window
void ResetStatus (void)
{
  wimp_window_state           sState;

  gnSent = 0;
  gnReceived = 0;
  SetIconText (Tag ("St0"), gwhStat, 0);
  SetIconText ("0", gwhStat, 1);
  SetIconText ("0", gwhStat, 3);
  SetIconText (gszServer, gwhStat, 5);

  sState.w = gwhStat;
  xwimp_get_window_state (& sState);
  xwimp_open_window ((wimp_open *)& sState);
}
